﻿using System;
using LambdaGp.Core;
using LambdaGp.Operators.Functions.MathFunctions;
using LambdaGp.Operators.Terminals;

namespace LambdaGp.Tests
{
    public static class TestPopulationFactory
    {
         public static Population CreatePopulation()
         {
             Population population = new Population();
             population.GenomeDefinition.MaxSize = 50;
             population.GenomeDefinition.MaxDepth = 7;
             population.GenomeDefinition.AddOperators(
                 new Add(),
                 new Div(),
                 new Sub(),
                 new Mul(),
                 new Pow(),
                 new Ephemeral(-5, +5));

             Variable x = population.GenomeDefinition.AddOperator(new Variable("x", 0.1));
             population.GenomeDefinition.AddOperator(new Variable("y", 2));
             population.GenomeDefinition.AddOperator(new Variable("z", 3));
             population.GenomeDefinition.MaxDepth = 5;
             population.PopulationSize = 50;
             population.ParsimonyPressure = 0.0;
             population.MaxGenerations = 50000;
             population.TargetFitness = 0.001;

             population.FitnessFunction =
             ind =>
             {
                 const double range = 2.0 * Math.PI;
                 const int steps = 250;
                 const double stepSize = 2.0 * range / steps;

                 int tests = 0;
                 x.DefaultValue = -range;
                 double errorSum = 0;

                 while (x.DefaultValue < range)
                 {
                     double targetValue = Target(x.DefaultValue);
                     double error = targetValue - ind.Evaluate();
                     errorSum += error * error;
                     x.DefaultValue += stepSize;
                     tests++;
                 }

                 return errorSum / tests;
             };

             return population;
         }

         private static double Target(double x)
         {
             return (x * x * 5) + (x * 3) + 5;
         }
    }
}